/*
 * Copyright (c) Huawei Device Co., Ltd. 2024. All rights reserved.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <securec.h>
#include "ef_parser.h"

#include "arf_manager.h"
#include "der_parser.h"
#include "common_utils.h"
#include "loghelper.h"

namespace OHOS {
namespace SE {
static const uint8_t SIM_FILE_INFO_MIN_DATA_LEN = 15;
static const uint8_t DATA_FILE_SIZE_OFFSET = 6;
static const uint8_t DATA_FILE_STRUCTURE_OFFSET = 13;
static const uint8_t DATA_FILE_RECORD_SIZE_OFFSET = 14;
static const uint8_t FILE_SIZE_FIRST_BYTE_OFFSET = 2;
static const uint8_t FILE_SIZE_SECOND_BYTE_OFFSET = 3;
static const uint8_t MIN_BUFFER_SIZE = 2;
static const uint8_t LOWEST_THREE_BIT_MASK = 0x07;
static const uint8_t FILE_TYPE_BIT_MASK = 0x38;
static const uint8_t DATA_RECORD_TOTAL_LEN = 5;
static const uint8_t DATA_RECORD_NUM_OFFSET = 4;
static const uint8_t DATA_RECORD_SIZE_OFFSET = 3;
static const uint8_t DATA_SIZE_DIVIDER = 2;
static const uint8_t FILE_ID_SIZE = 2;
static const uint8_t RESP_SW1_OFFSET = 2;
static const uint8_t RESP_SW2_OFFSET = 1;
static const uint8_t MAX_DATA_SIZE = 253;

EfParser::EfParser()
{
}

EfParser::~EfParser()
{
}

void EfParser::ParseFileData(const std::vector<uint8_t> &response)
{
    if (response.size() == 0) {
        ErrorLog("Invalid file info response.");
        return;
    }
    if (response[0] == ASN1_FCP) {
        ParseUSIMFileData(response);
    } else {
        ParseSIMFileInfo(response);
    }
}

void EfParser::ParseSIMFileInfo(const std::vector<uint8_t> &response)
{
    if (response.size() < SIM_FILE_INFO_MIN_DATA_LEN) {
        ErrorLog("Invalid file info response length.");
        return;
    }

    // parse file type
    if (response[DATA_FILE_SIZE_OFFSET] == FILE_TYPE_EF) {
        fileType_ = FILE_TYPE_EF;
    } else {
        fileType_ = FILE_TYPE_UNKNOWN;
    }

    // parse file structure
    if (response[DATA_FILE_STRUCTURE_OFFSET] == FILE_STRUCTURE_TRANSPARENT) {
        fileStructType_ = FILE_STRUCTURE_TRANSPARENT;
    } else if (response[DATA_FILE_STRUCTURE_OFFSET] == FILE_STRUCTURE_LINEAR_FIXED) {
        fileStructType_ = FILE_STRUCTURE_LINEAR_FIXED;
    } else {
        fileStructType_ = FILE_STRUCTURE_UNKNOWN;
    }

    // parse file size
    fileDataSize_ = (response[FILE_SIZE_FIRST_BYTE_OFFSET] << ONE_BYTE_SHIFT) + response[FILE_SIZE_SECOND_BYTE_OFFSET];

    // parse other infomation
    if (fileType_ == FILE_TYPE_EF && fileStructType_ != FILE_STRUCTURE_TRANSPARENT) {
        fileRecordSize_ = response[DATA_FILE_RECORD_SIZE_OFFSET];
        if (fileRecordSize_ > 0) {
            fileRecordNum_ = fileDataSize_ / fileRecordSize_;
        } else {
            ErrorLog("ParseSIMFileInfo fileRecordNum_ denominator cannot be 0.");
        }
    }
    InfoLog("file data size = 0x%{public}x, file record num = 0x%{public}d, record size each = 0x%{public}x",
        fileDataSize_, fileRecordNum_, fileRecordSize_);
}

void EfParser::ParseUSIMFileData(const std::vector<uint8_t> &response)
{
    std::shared_ptr<DerParser> der = std::make_shared<DerParser>(response);
    der->GetTlvDataLength(ASN1_FCP);
    while (!der->IsParsingEnd()) {
        uint8_t tlvType = der->GetTlvType();
        if (tlvType == TLV_TYPE_FILE_SIZE) {
            ParseUSIMFileDataSize(der->GetTlvData());
        } else if (tlvType == TLV_TYPE_FILE_INFO) {
            ParseUSIMFileInfo(der->GetTlvData());
        } else {
            der->SkipTlvData();
        }
    }
}

void EfParser::ParseUSIMFileDataSize(const std::vector<uint8_t> &tlvData)
{
    if (tlvData.size() < MIN_BUFFER_SIZE) {
        ErrorLog("invalid tlv data size [%{public}lu]", tlvData.size());
        return;
    }
    fileDataSize_ = (tlvData[0] << ONE_BYTE_SHIFT) | tlvData[1];
}

void EfParser::ParseUSIMFileInfo(const std::vector<uint8_t> &tlvData)
{
    if (tlvData.size() < MIN_BUFFER_SIZE) {
        ErrorLog("invalid tlv data size [%{public}lu]", tlvData.size());
        return;
    }

    // parse file type
    if ((tlvData[0] & FILE_TYPE_BIT_MASK) == FILE_TYPE_BIT_MASK) {
        fileType_ = FILE_TYPE_UNKNOWN;
    } else {
        fileType_ = FILE_TYPE_EF;
    }

    // parse file structure
    if ((tlvData[0] & LOWEST_THREE_BIT_MASK) == 0x01) {
        fileStructType_ = FILE_STRUCTURE_TRANSPARENT;
    } else if ((tlvData[0] & LOWEST_THREE_BIT_MASK) == 0x02) {
        fileStructType_ = FILE_STRUCTURE_LINEAR_FIXED;
    } else {
        fileStructType_ = FILE_STRUCTURE_UNKNOWN;
    }

    // parse other infomation
    if (tlvData.size() == DATA_RECORD_TOTAL_LEN) {
        fileRecordSize_ = tlvData[DATA_RECORD_SIZE_OFFSET];
        fileRecordNum_ = tlvData[DATA_RECORD_NUM_OFFSET];
    }
    InfoLog("file data size = 0x%{public}x, file record num = 0x%{public}d, record size each = 0x%{public}x",
        fileDataSize_, fileRecordNum_, fileRecordSize_);
}

void EfParser::ResetFileInfo()
{
    fileType_ = FILE_TYPE_UNKNOWN;
    fileStructType_ = FILE_STRUCTURE_UNKNOWN;
    fileId_ = 0;
    fileDataSize_ = 0;
    fileRecordSize_ = 0;
    fileRecordNum_ = 0;
    filePath_.clear();
}

bool EfParser::ReadFileByPath(const std::vector<uint8_t> &path, uint16_t &rspApdu)
{
    rspApdu = 0;
    if ((path.size() == 0) || ((path.size() % DATA_SIZE_DIVIDER) != 0)) {
        ErrorLog("inavlid path");
        return false;
    }
    ResetFileInfo();
    uint32_t index = 0;
    uint32_t length = path.size();
    if ((ArfManager::GetInstance().GetUsedSeName() == SE_NAME_SIM ||
        ArfManager::GetInstance().GetUsedSeName() == SE_NAME_SIM2) && (length > MIN_BUFFER_SIZE)) {
        index = length - FILE_ID_SIZE;
        filePath_ = path;
    }
    std::vector<uint8_t> response;
    for (; index < length; index += MIN_BUFFER_SIZE) {
        InfoLog("ReadFileByPath, path[0x%{public}x][0x%{public}x]", path[index], path[index + 1]);
        std::vector<uint8_t> command = {0x00, 0xA4, 0x00, 0x04, 0x02};
        command.push_back(path[index]);
        command.push_back(path[index + 1]);

        response = ArfManager::GetInstance().TransmitArfApdu(shared_from_this(), command);
        if (response.size() < MIN_BUFFER_SIZE) {
            ErrorLog("invalid response.");
            return false;
        }

        uint8_t sw1 = response[response.size() - RESP_SW1_OFFSET];
        uint8_t sw2 = response[response.size() - RESP_SW2_OFFSET];
        if ((sw1 != 0x62) && (sw1 != 0x63) && (sw1 != 0x90) && (sw1 != 0x91)) {
            rspApdu = static_cast<uint16_t>((sw1 << ONE_BYTE_SHIFT) + sw2);
            return true;
        }
    }
    ParseFileData(response);
    rspApdu = APDU_SUCCESS;
    return true;
}

std::vector<uint8_t> EfParser::ReadFileByDataSize(uint32_t dataOffset, int dataSize)
{
    std::vector<uint8_t> fileInfo;
    if (fileDataSize_ == 0) {
        return fileInfo;
    }
    if (dataSize == -1) {
        dataSize = fileDataSize_;
    }
    if (fileType_ != FILE_TYPE_EF) {
        ErrorLog("inavlid file type [%{public}x]", fileType_);
        return fileInfo;
    }
    if (fileStructType_ != FILE_STRUCTURE_TRANSPARENT) {
        ErrorLog("inavlid file structure type [%{public}x]", fileStructType_);
        return fileInfo;
    }

    uint8_t length = 0;
    int currentIndex = 0;
    while (dataSize > 0) {
        if (dataSize < MAX_DATA_SIZE) {
            length = static_cast<uint8_t>(dataSize);
        } else {
            length = MAX_DATA_SIZE;
        }
        InfoLog("ReadFileByDataSize, dataOffset[0x%{public}x], length[0x%{public}x]", dataOffset, length);

        std::vector<uint8_t> command = {0x00, 0xB0};
        command.push_back(static_cast<uint8_t>(dataOffset >> ONE_BYTE_SHIFT));
        command.push_back(static_cast<uint8_t>(dataOffset));
        command.push_back(length);

        std::vector<uint8_t> response = ArfManager::GetInstance().TransmitArfApdu(shared_from_this(), command);
        fileInfo.resize(currentIndex + length);
        if (memcpy_s(&fileInfo[currentIndex], length, &response[0], length) != 0) {
            ErrorLog("memcpy_s failed.");
            return std::vector<uint8_t>();
        }

        dataSize -= length;
        dataOffset += length;
        currentIndex += length;
    }
    return fileInfo;
}

std::vector<uint8_t> EfParser::ReadFileRecord(uint8_t record)
{
    std::vector<uint8_t> result;
    if (fileType_ != FILE_TYPE_EF) {
        ErrorLog("inavlid file type");
        return result;
    }
    if (fileStructType_ != FILE_STRUCTURE_LINEAR_FIXED) {
        ErrorLog("inavlid file structure");
        return result;
    }
    if (record > fileRecordNum_) {
        ErrorLog("inavlid record number");
        return result;
    }

    InfoLog("ReadFileRecord, record[%{public}x], file record size[%{public}x]", record, fileRecordSize_);
    std::vector<uint8_t> command = {0x00, 0xB2, record, 0x04, fileRecordSize_};

    std::vector<uint8_t> response = ArfManager::GetInstance().TransmitArfApdu(shared_from_this(), command);
    result.resize(fileRecordSize_);
    std::copy(response.begin(), response.end(), result.begin());
    return result;
}

int EfParser::GetFileId()
{
    return fileId_;
}

std::vector<uint8_t> EfParser::GetFilePath()
{
    return filePath_;
}

uint8_t EfParser::GetFileRecordNumber()
{
    return fileRecordNum_;
}
}  // namespace SE
}  // namespace OHOS
